using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using FreedBack.Core.Entities;
using System.Data.Entity.ModelConfiguration.Conventions;
using Freedback.Core.Repositories;
using System.Linq.Expressions;

namespace Freedback.Core.Context
{
    public class FreedbackContext : DbContext, IFreedbackContext, IUnitOfWork
    {
        static FreedbackContext()
        {
            QueryableExtensions.Includer = new EntityIncluder();
        }

        public FreedbackContext() : base("FreedbackContext")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }

        public DbSet<FeedbackForm> FeedbackForms { get; set; }
        IQueryable<FeedbackForm> IFreedbackContext.FeedbackForms { get { return FeedbackForms; } }

        public DbSet<FeedbackType> FeedbackTypes { get; set; }
        IQueryable<FeedbackType> IFreedbackContext.FeedbackTypes { get { return FeedbackTypes; } }

        public DbSet<AreaOfFeedback> AreasOfFeedback { get; set; }
        IQueryable<AreaOfFeedback> IFreedbackContext.AreasOfFeedback { get { return AreasOfFeedback; } }

        public DbSet<RatingValue> RatingValues { get; set; }
        IQueryable<RatingValue> IFreedbackContext.RatingValues { get { return RatingValues; } }

        void IUnitOfWork.Save()
        {
            base.SaveChanges();
        }

        T IGenericRepository.Get<T>(int id)
        {
            return Set<T>().Find(id);
        }

        T IGenericRepository.Get<T>(int id, params Expression<Func<T, object>>[] associations)
        {
            if (associations.Any())
            {
                var query = associations.Aggregate(Set<T>().AsQueryable(), (current, path) => current.Include(path));
                return query.SingleOrDefault(i => i.Id == id);
            }

            return Set<T>().Find(id);
        }

        void IGenericRepository.Add<T>(T entity)
        {
            Set<T>().Add(entity);
        }

        IEnumerable<T> IGenericRepository.GetAll<T>()
        {
            // I do not want the result of GetAll to be IQueriable<T> otherwise dev may just start using GetAll from everywhere instead of implementing queries properly
            return Set<T>().ToList();
        }

        T IGenericRepository.Get<T>(object id)
        {
            return Set<T>().Find(id);
        }

        T IGenericRepository.Get<T>(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] associations)
        {
            var query = associations.Aggregate(Set<T>().AsQueryable(), (current, path) => current.Include(path));
            return query.SingleOrDefault(predicate);
        }

        void IGenericRepository.Delete<T>(int id)
        {
            var existing = Set<T>().Find(id);
            Set<T>().Remove(existing);
        }
    }
}